package com.raidan.dclog.core.parser.text;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.raidan.dclog.core.Config;
import com.raidan.dclog.core.Secundomer;
import com.raidan.dclog.core.parser.IParser;
import com.raidan.dclog.core.parser.ParseLineException;

/**
 * Base class for all parsers
 * 
 * @author raidan
 */
public abstract class AbstractTextParser implements IParser {

	private Pattern pattern;
	private SimpleDateFormat dateFormatter;

	protected Pattern getLinePattern() {
		return this.pattern;
	}

	protected SimpleDateFormat getDateTimeFormatter() {
		return this.dateFormatter;
	}

	private int bulkPos;
	private int totalRows;
	private int currentIncorrectLines;

	private final static Logger logger = LoggerFactory.getLogger(AbstractTextParser.class);

	private final static boolean NO_COMMIT = false;

	/**
	 * Implementation of parsing
	 */
	public void parse(final Connection connection) throws SQLException, ParseLineException, IOException {
		final String fileName = this.getFileName();

		if (fileName == null || fileName.trim().length() == 0) {
			throw new IllegalStateException("Method getFileName does not return any file name.");
		}
		this.pattern = Pattern.compile(this.getPattern());
		this.dateFormatter = new SimpleDateFormat(this.getDateTimeFormat());

		String destFile = Config.getInstance().getBaseLogDirectory() + fileName;

		logger.info("Loading '{}' from '{}'...", this.getName(), destFile);

		Secundomer loadTime = new Secundomer();
		loadTime.start();

		final PreparedStatement statement = connection.prepareStatement(getSQLInsert());
		this.bulkPos = 0;
		this.totalRows = 0;

		final int maxIncorrectLines = Config.getInstance().getMaxIncorrectLines();
		this.currentIncorrectLines = 0;

		final boolean commitEveryBatch = Config.getInstance().isCommitEveryBatch();
		final int batchBulkSize = Config.getInstance().getBatchBulkSize();

		try {
			new AbstractTextReader() {

				@Override
				public void readLine(int linePos, String line) throws SQLException, ParseLineException {
					try {
						processLine(linePos, line, statement);
					} catch (ParseLineException ple) {

						// Count errors
						// We can pass some rows
						currentIncorrectLines++;
						if (maxIncorrectLines != 0 && currentIncorrectLines > maxIncorrectLines) {
							throw ple;
						} else {
							if (maxIncorrectLines != 0) {
								logger.warn("Found error during loading row " + linePos + " of file '" + fileName
										+ "': " + ple.getMessage());
							}
						}
					}
					totalRows++;
					bulkPos++;
					if (bulkPos > batchBulkSize) {
						if (NO_COMMIT) {
							statement.clearBatch();
						} else {
							statement.executeBatch();
						}

						if (commitEveryBatch) {
							connection.commit();
						}
						bulkPos = 0;
					}
				}
			}.parseImpl(destFile);

			if (NO_COMMIT) {
				statement.clearBatch();
			} else {
				statement.executeBatch();
			}

			if (commitEveryBatch) {
				connection.commit();
			}

			loadTime.stop();

			if (logger.isDebugEnabled()) {
				logger.debug(" .. Loaded "
						+ totalRows
						+ " lines at "
						+ loadTime.getTotalTime()
						+ " msec. "
						+ (this.currentIncorrectLines > 0 ? "Found " + this.currentIncorrectLines + " incorrect lines."
								: ""));
			}
		} finally {
			statement.close();
		}
	}

	protected abstract String getPattern();

	protected abstract String getFileName();

	protected String getDateTimeFormat() {
		return "yyyy-MM-dd HH:mm";
	}

	protected String getTimeFormat() {
		return "HH:mm:ss";
	}

	protected abstract String getSQLInsert();

	protected abstract void processLine(int linePos, String line, PreparedStatement statement) throws SQLException,
			ParseLineException;

	public abstract String getName();
}
